
package com.probiz.estore.imports.service.impl;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

import com.probiz.estore.common.helper.ConfigUtil;
import com.probiz.estore.common.util.DateUtil;
import com.probiz.estore.core.util.I18nUtil;
import com.probiz.estore.core.util.StringUtil;
import com.probiz.estore.imports.handler.ColumnHandler;
import com.probiz.estore.imports.handler.PersistenceHandler;
import com.probiz.estore.imports.model.Column;
import com.probiz.estore.imports.model.ImportInfo;
import com.probiz.estore.imports.model.ImportMappingConfig;
import com.probiz.estore.imports.model.ImportModel;
import com.probiz.estore.imports.service.BaseImportManager;
import com.probiz.estore.webapp.util.RequestContext;

/**
 * 
 * 专门处理XSL格式
 *
 */
public class BaseImportManagerImpl implements BaseImportManager {
	protected Logger	logger	= Logger.getLogger(BaseImportManagerImpl.class);
	
	//import XML 文件配置的信息
	protected ImportMappingConfig importConfig ;
	
	
	//用于获取当前导入信息
	protected ImportInfo	importInfo	;
	//导入的线程
	protected Thread importThread;
	
	/**
	 * 加入“停止信号”和“暂停信号”是因为Thread.stop,Thread.suspend,Thread.resume是不安全的，会导致死锁。
	 */
	//停止信号
	protected boolean stopSignal = false;
	//暂停信号
	protected boolean suspendSignal = false;
	
	
	public void setImportConfig(ImportMappingConfig importConfig) {
		this.importConfig = importConfig;
	}
	
	public ImportMappingConfig getImportConfig() {
		return importConfig;
	}
	
	public ImportInfo getImportInfo() {
		return importInfo;
	}

	/*
	 * (non-Javadoc)
	 * @see com.probiz.estore.imports.service.BaseImportManager#startImport(java.lang.String, java.lang.String)
	 */
	public void startImport(String file,String encoding, Integer appUserId){
		//新的任务
		stopSignal = true;
		suspendSignal = false;
		while(importThread!=null && importThread.isAlive()){
			//将旧线程停止
			//...wait
		}
		stopSignal = false;
		importThread = new Thread(new ImportThread(file,encoding, appUserId));
		importThread.start();
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.probiz.estore.imports.service.BaseImportManager#stopImport()
	 */
	public void stopImport(){
		if(importThread!=null && importThread.isAlive()){
			stopSignal = true;
			suspendSignal = false;
			importInfo.setStatus(ImportInfo.STATUS_USERSTOP);
		}
	}
	/*
	 * (non-Javadoc)
	 * @see com.probiz.estore.imports.service.BaseImportManager#suspendImport()
	 */
	public void suspendImport(){
		if(importThread!=null && importThread.isAlive()){
			suspendSignal = true;
			stopSignal = false;
			importInfo.setStatus(ImportInfo.STATUS_USERSUSPEND);
		}
	}
	/*
	 * (non-Javadoc)
	 * @see com.probiz.estore.imports.service.BaseImportManager#resumeImport()
	 */
	public void resumeImport(){
		if(importThread!=null && importThread.isAlive()){
			suspendSignal = false;
			stopSignal = false;
			importInfo.setStatus(ImportInfo.STATUS_INPROCESS);
		}
	}
	/*
	 * (non-Javadoc)
	 * @see com.probiz.estore.imports.service.BaseImportManager#newTask()
	 */
	public void newTask(){
		stopImport();
		importInfo = null;
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.probiz.estore.imports.service.BaseImportManager#preview(java.lang.String, java.lang.String)
	 */
	public List<List<String>> preview(String file,String encoding) {
		List<List<String>> result=new ArrayList<List<String>>();

	    try {
	      Workbook wb = WorkbookFactory.create(new FileInputStream(new File(getFullFile(file))));
	      Sheet  sheet = wb.getSheetAt(0);
	      if(sheet.getLastRowNum()>0){
		      short lastCellNum = sheet.getRow(0).getLastCellNum();
		      for(Row row : sheet){    
		    	  List<String> dataCellList=new ArrayList<String>();
		          for(short cellnum =0 ; cellnum<lastCellNum; cellnum++){
		        	  Cell cell = row.getCell(cellnum);
		        	  if(null != cell){
		        		  dataCellList.add(getCellValue(row.getCell(cellnum)));
		        	  }else{
		        		  dataCellList.add("");
		        	  }
		          }
	        	  result.add(dataCellList);
	        	  
	        	  //只列出前200条
	        	  if(row.getRowNum()>=200){
	        		  break;
	        	  }
		      }    
	      }   
	    } catch (Exception e) {
	      e.printStackTrace();
	    }finally{
	    }
	    
	    return result;
	}
	
	private String getCellValue(Cell cell){
		switch(cell.getCellType()) {
	      case Cell.CELL_TYPE_STRING:
	    	  	return cell.getRichStringCellValue().getString().trim();
	      case Cell.CELL_TYPE_NUMERIC:
		        if(org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) {
		          return DateUtil.convertDateTimeToString(cell.getDateCellValue());
		        } else {
		          //注意先将其转换为String类型
		          cell.setCellType (Cell.CELL_TYPE_STRING);
		          return cell.getStringCellValue();
		        }
	      case Cell.CELL_TYPE_BOOLEAN:
	    	  	return String.valueOf(cell.getBooleanCellValue());
	      case Cell.CELL_TYPE_FORMULA:
	    	  	return cell.getCellFormula().trim();
	      case Cell.CELL_TYPE_BLANK:
	    	  	return "";
	      case Cell.CELL_TYPE_ERROR:
	    	  	return "ERROR";
	      default:
	    	    return "";
	    }

	}
	
	/**
	 * 获取列头字段列表
	 * @param metaData
	 * @return
	 * @throws Exception
	 */
	protected List<String> getMetaCellList(Sheet sheet) throws Exception{
		List<String> metaCellList=new ArrayList<String>();
		if(sheet.getLastRowNum()>0){
			Row row = sheet.getRow(0);
			for(Cell cell : row){
				metaCellList.add(getCellValue(cell));
			}
		}
		return metaCellList;
	}
	
	/**
	 * 获得一行数据的Map，相当于json格式
	 * @param metaData
	 * @param rs
	 * @return
	 * @throws Exception
	 */
	protected Map<String,String> getRowDataMap(List<String> realHeaders,Row row) {
		Map<String,String> rowDataMap=new LinkedHashMap<String, String>();
		for(int i=0;i<realHeaders.size();i++){
			Cell cell = row.getCell(i);
			if(null != cell){
				rowDataMap.put(realHeaders.get(i), getCellValue(cell));
			}else{
				rowDataMap.put(realHeaders.get(i), "");
			}
		}
		return rowDataMap;
	}
	
	
	/*
	 * (non-Javadoc)
	 * @see com.probiz.estore.imports.service.BaseImportManager#countFileRowSize(java.lang.String, java.lang.String)
	 */
	public Integer countFileRowSize(String file,String encoding){
		try {
	      Workbook wb = WorkbookFactory.create(new FileInputStream(new File(getFullFile(file))));
	      Sheet  sheet = wb.getSheetAt(0);
	      return sheet.getLastRowNum();
	    } catch (Exception e) {
	      e.printStackTrace();
	    }finally{
	    }
	    return 0;
	}
	

	
	/**
	 * 获得文件名
	 * @param file
	 * @return
	 */
	protected String getFileName(String file){
		String fileName=file.substring(file.lastIndexOf("/")+1,file.indexOf("."));
		return fileName;
	}
	/**
	 * 获得文件路径
	 * @param file
	 * @return
	 */
	protected String getFilePath(String file){
		String path=ConfigUtil.getInstance().getMediaStorePath()+"/"+file.substring(0,file.lastIndexOf("/"));
		return path;
	}
	/**
	 * 获得文件系统完整路径
	 * @param file
	 * @return
	 */
	protected String getFullFile(String file){
		String fullFile = ConfigUtil.getInstance().getMediaStorePath()+"/"+file;
		return fullFile;
	}
	
	
	/**
	 * 使用内部线程类
	 *
	 */
	protected class ImportThread implements Runnable{
		private String file;
		private String encoding;
		private Integer appUserId;
		protected ImportThread(){
			
		}
		public ImportThread(String file,String encoding, Integer appUserId){
			this.file = file;
			this.encoding = encoding;
			this.appUserId = appUserId;
			//鏂板疄渚�
			importInfo = ImportInfo.newInstance(file,encoding);
		}
		public void run(){
			try {
				doImportAll(file,encoding,appUserId);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	
	/**
	 * 导入文件
	 * @param file
	 * @param encoding
	 */
	protected void doImportAll(String file,String encoding,Integer appUserId) throws Exception{
		//--------------start: 导入前(输入输出流准备)--------------
		//导入文件输入流
		Workbook wb = WorkbookFactory.create(new FileInputStream(new File(getFullFile(file))));
		Sheet  sheet = wb.getSheetAt(0);
		
		//失败文件输出流
		FileOutputStream failFileBw = new FileOutputStream(importInfo.getFailFile());
		Workbook failFileWorkbook = new HSSFWorkbook();
		Sheet failFileSheet = failFileWorkbook.createSheet();
		
		//日志文件输出流
		BufferedWriter logBw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(importInfo.getLogFile())));
		
		//错误日志文件输出流
		BufferedWriter failLogBw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(importInfo.getFailLogFile())));
		
		
		StringBuffer sb = new StringBuffer(1000);
		//--------------end:   导入前(输入输出流准备)--------------

		
		//--------------start: 导入前(数据构建)--------------
		
		//设置真实列头
		List<String> realHeaders= getMetaCellList(sheet);
		importConfig.setRealHeaders(realHeaders);
		
		//获得CONFIG的列模型
		List<Column> columns = importConfig.getAllColumns();
		
		//设置需要处理的行数
		importInfo.setImportFileRowCount(countFileRowSize(file,encoding));
		
		//--------------end  : 导入前(数据构建)--------------
		
		
		//-----------------start: 导入中--------------------
		//日志
		sb.append("【开始导入】 " +  I18nUtil.getInstance().getMessage("import.file.data",new Object[] {  file,encoding })+"\n");
		logger.info(sb.toString());
		logBw.write(sb.toString());
		
		
		//导入文件真正的行数（序列）
		Integer rowNum = 1;
		//导入文件错误的行计数
		Integer failRowCount = 0;
		//失败文件写入列头
		writeFailFileHeaders(failFileSheet,realHeaders);
		
		for(Row row : sheet){
			
    		//--------start:线程控制-------
			if(stopSignal){
				//收到停止信号
				importInfo.setStatus(ImportInfo.STATUS_USERSTOP);
				break;
			}
            while (suspendSignal){
            	//收到暂停信号
            	importInfo.setStatus(ImportInfo.STATUS_USERSUSPEND);
            	Thread.currentThread().sleep(500);
            }
            //设置为执行中
            importInfo.setStatus(ImportInfo.STATUS_INPROCESS);
            //--------end:  线程控制-------
            
            //列头不处理
			if(row.getRowNum()==0)continue;
			//真正行数自增一
			rowNum++;
			//设置当前行数
			importInfo.setCurrentRow(rowNum-1);
			//设置结束行数
			importInfo.setEndRow(rowNum);
			//获得当前行JSON结构对象
			Map<String,String> rowDataMap=getRowDataMap(realHeaders, row);
			
			//-------------------------start:导入一行数据-------------------------------
			ImportModel importModel = doImportTarget(importConfig, columns, rowNum, rowDataMap,logBw,failLogBw, appUserId);
			//-------------------------end:  导入一行数据-------------------------------

			//case:该行失败，写入数据到失败文件
			if(ImportModel.RESULT_ERROR.equals(importModel.getResult())){	
				failRowCount++;
				writeFailRowData(failFileSheet,row,failRowCount);
			}	

			//case:超过连续失败数，停止导入
			if(importInfo.getContinuouslyFailCount() > ConfigUtil.getInstance().getContinuouslyFailCountStopImport()){
				importInfo.setStatus(ImportInfo.STATUS_FAIL);
				break;
			}
				
		}
		//-----------------end:   导入中--------------------
		
		
		//-----------------start: 导入完成--------------------
		//设置结束行数
		importInfo.setEndRow(rowNum);
		//设置结束时间
		importInfo.setEndImportTime(new Date());
		
		//如果没有设置返回状态或者当前是进行中状态，则设置为成功状态
		if(null==importInfo.getStatus() || ImportInfo.STATUS_INPROCESS.equals(importInfo.getStatus())){
			importInfo.setStatus(ImportInfo.STATUS_SUCCESS);
		}
		//-----------------end:   导入完成--------------------
		
		
		//日志
		sb.delete(0, sb.length());
		sb.append("\n【结束导入】 完成情况汇总。\n");
		sb.append("开始时间："+DateUtil.convertDateTimeToString(importInfo.getStartImportTime())+"\n");
		sb.append("结束时间："+DateUtil.convertDateTimeToString(importInfo.getEndImportTime())+"\n");
		sb.append("添加成功数量："+importInfo.getSuccessInsertCount()+"\n");
		sb.append("更新成功数量："+importInfo.getSuccessUpdateCount()+"\n");
		sb.append("添加失败数量："+importInfo.getFailInsertCount()+"\n");
		sb.append("更新失败数量："+importInfo.getFailUpdateCount()+"\n");
		sb.append("忽略处理数量："+importInfo.getIgnoreCount()+"\n");
		sb.append("连续失败次数："+importInfo.getContinuouslyFailCount()+"\n");
		sb.append("开始导入的行："+importInfo.getBeginRow()+"\n");
		sb.append("结束导入的行："+importInfo.getEndRow()+"\n");
		sb.append("错误的行：" + StringUtil.integerArrayToString(importInfo.getFailData().toArray(new Integer[0]), ",")+"\n");
		logger.info(sb.toString());
		logBw.write(sb.toString()+"\n");
		
		//写入失败文件
		failFileWorkbook.write(failFileBw);
		failFileBw.close();
		
		logBw.close();
		failLogBw.close();
		
	}
	
	
	/**
	 * 导入处理数据文件某一行
	 * @param importMappingConfig
	 * @param columns
	 * @param rowNum
	 * @param rowDataMap
	 * @param logBw
	 * @param failLogBw
	 * @return
	 * @throws IOException
	 */
	protected ImportModel doImportTarget(ImportMappingConfig importMappingConfig, List<Column> columns,Integer rowNum,Map<String,String>rowDataMap,BufferedWriter logBw,BufferedWriter failLogBw, Integer appUserId) throws Exception {
		ImportModel importModel=new ImportModel();
		importModel.setAppUserId(appUserId);
		//日志
		String msg =  "\n第【"+rowNum+"】行 开始执行:"+rowDataMap+"\n";
		logger.info(msg);
		logBw.write(msg);
		
		//导入处理的对象
		Object target=Class.forName(importMappingConfig.getTarget()).newInstance();
		importModel.setTarget(target);
		//默认设置为新增
		importModel.setUpdate(false);

		//导入的数据文件真实的列头
		List<String> realHeaders=importMappingConfig.getRealHeaders();
		try {
			//--------------start:设置要处理对象的值----------------
			nextRow: for (Column column : columns) {
				column.setRowDataMap(rowDataMap);
				column.setRowNum(rowNum);
				for (int i = 0; i < realHeaders.size(); i++) {
					//当配置的列与导入文件列头一致的就处理该数据
					if (realHeaders.get(i).trim().equals(column.getColumnHeader())|| column.isInitData()|| (StringUtils.isNotEmpty(column.getColumnHeaderSplit())&&ArrayUtils.indexOf(column.getColumnHeader().split(column.getColumnHeaderSplit()), realHeaders.get(i).trim())==0)) {
						ColumnHandler handler = column.getHandler();
						//设置获取当前单元格值
						column.setValue(rowDataMap.get(realHeaders.get(i)),importModel);
						
						if (!ImportModel.RESULT_ERROR.equals(importModel.getResult()) && handler!=null) {
							handler.setProperty(importModel,column);
						}
						
						if (column.isInitData()) {
							break;
						}
						
						//出错 则不执行该行
						if (ImportModel.RESULT_ERROR.equals(importModel.getResult())) {
							break nextRow;
						}
					}
				}
			}
			//--------------end:  设置要处理对象的值----------------
		
    		//--------------start:执行持久化方法-------------------
			if (!ImportModel.RESULT_ERROR.equals(importModel.getResult())) {
				PersistenceHandler persistenceHandler=importMappingConfig.getPersistenceHandler();
				//验证
				persistenceHandler.validate(importModel);
				if (!ImportModel.RESULT_ERROR.equals(importModel.getResult())) {
					//执行
					persistenceHandler.saveOrUpdate(importModel);
				}
			}
			
			//日志
			if(ImportModel.RESULT_SUCCESS.equals(importModel.getResult())){
				importInfo.addSuccessCount(importModel.isUpdate());
				
				msg = "成功导入数据。\n";
				logger.info(msg);
				logBw.write(msg);
			}else if (ImportModel.RESULT_ERROR.equals(importModel.getResult())){
				importInfo.addFailCount(importModel.isUpdate(),rowNum);
				
				logger.info(importModel.getMsg());
				logBw.write(importModel.getMsg()+"\n");
				failLogBw.write("第【"+rowNum+"】行 "+importModel.getMsg()+"\n");
				failLogBw.flush();
			}
			
			msg = "第【"+rowNum+"】行 结束执行\n";
			logger.info(msg);
			logBw.write(msg);
			logBw.flush();
			
			//--------------end:  执行持久化方法-------------------
			
		} catch (Exception e) {
			importModel.setResult(ImportModel.RESULT_ERROR);
			msg = "错误信息："+e.getMessage()+"\n";
			logger.warn(msg);
			logBw.write(msg);
			failLogBw.write("第【"+rowNum+"】行 "+msg+"\n");
		}
		return importModel;
		
	}
	
	/**
	 * 失败文件 写入列头
	 * @param sheet
	 * @param realHeaders
	 */
	protected void writeFailFileHeaders(Sheet sheet,List<String> realHeaders)	{
		Row row = sheet.createRow(0);   
		for(int i=0; i< realHeaders.size(); i++){
			Cell cell = row.createCell(i);
			cell.setCellValue(realHeaders.get(i));
		}
	}
	/**
	 * 失败文件 写入行数据
	 * @param sheet
	 * @param row
	 * @param failRowCount
	 */
	protected void writeFailRowData( Sheet sheet, Row row,Integer failRowCount) {
		Row newRow = sheet.createRow(failRowCount);
		for(Cell cell: row){
			Cell newCell = newRow.createCell(cell.getColumnIndex());
			newCell.setCellValue(getCellValue(cell));
		}
	}





}
